//-
// ==========================================================================
// Copyright (C) 1995 - 2006 Autodesk, Inc. and/or its licensors.  All 
// rights reserved.
//
// The coded instructions, statements, computer programs, and/or related 
// material (collectively the "Data") in these files contain unpublished 
// information proprietary to Autodesk, Inc. ("Autodesk") and/or its 
// licensors, which is protected by U.S. and Canadian federal copyright 
// law and by international treaties.
//
// The Data is provided for use exclusively by You. You have the right 
// to use, modify, and incorporate this Data into other products for 
// purposes authorized by the Autodesk software license agreement, 
// without fee.
//
// The copyright notices in the Software and this entire statement, 
// including the above license grant, this restriction and the 
// following disclaimer, must be included in all copies of the 
// Software, in whole or in part, and all derivative works of 
// the Software, unless such copies or derivative works are solely 
// in the form of machine-executable object code generated by a 
// source language processor.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND. 
// AUTODESK DOES NOT MAKE AND HEREBY DISCLAIMS ANY EXPRESS OR IMPLIED 
// WARRANTIES INCLUDING, BUT NOT LIMITED TO, THE WARRANTIES OF 
// NON-INFRINGEMENT, MERCHANTABILITY OR FITNESS FOR A PARTICULAR 
// PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE, OR 
// TRADE PRACTICE. IN NO EVENT WILL AUTODESK AND/OR ITS LICENSORS 
// BE LIABLE FOR ANY LOST REVENUES, DATA, OR PROFITS, OR SPECIAL, 
// DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES, EVEN IF AUTODESK 
// AND/OR ITS LICENSORS HAS BEEN ADVISED OF THE POSSIBILITY 
// OR PROBABILITY OF SUCH DAMAGES.
//
// ==========================================================================
//+

#include <stdio.h>
#include <iostream>

#include <D3DViewportRenderer.h>

#include <maya/MGlobal.h>
#include <maya/MString.h>
#include <maya/MRenderingInfo.h>
#include <maya/MRenderTarget.h>
#include <maya/MFnCamera.h>
#include <maya/MAngle.h>
#include <maya/MPoint.h>
#include <maya/MVector.h>
#include <maya/MItDag.h>
#include <maya/MMatrix.h>
#include <maya/MDagPath.h>
#include <maya/MFnDagNode.h>
#include <maya/MFnMesh.h>
#include <maya/MItMeshPolygon.h>
#include <maya/MBoundingBox.h>
#include <maya/MImage.h>
#include <maya/MDrawTraversal.h>
#include <maya/MGeometryManager.h>
#include <maya/MGeometry.h>
#include <maya/MGeometryData.h>
#include <maya/MGeometryPrimitive.h>
#include <maya/MNodeMessage.h> // For monitor geometry list
#include <maya/MPlug.h>
#include <maya/MPlugArray.h>
#include <maya/MFnSet.h>
#include <maya/MFnNumericData.h>
#include <maya/MItDependencyGraph.h>
#include <maya/MMatrix.h>

#include <stdio.h>
#include <maya/MFnLight.h>
#include <maya/MFnSpotLight.h>

#include <maya/MPxHardwareShader.h>
#include <maya/MRenderProfile.h>

#if defined(D3D9_SUPPORTED)

// Screen space quad vertex
struct ScreenSpaceVertex
{
    D3DXVECTOR4 position; // position
    D3DXVECTOR2 texCoord; // texture coordinate

    static const DWORD FVF;
};
const DWORD ScreenSpaceVertex::FVF = D3DFVF_XYZRHW | D3DFVF_TEX1;

//////////////////////////////////

//
// Class : D3DViewportRenderer
//
// Very simple renderer using D3D to render to an offscreen render target.
// The contents are read back into system memory to blit into an OpenGL context.
//
// This example has been test compiled against the both the Feb. and April 2006 
// DirectX developer SDKs. Define the D3D9_SUPPORTED preprocessor directive 
// to compile D3D code in.
//
// These code items are work in progress:
//
// - camera is fixed to be perspective. No orthographic cameras yet.
// - offscreen surface is fixed in size.
// - surfaces can either be fixed RGBA8888 or floating point 16. Final output
//   is always fixed, though post-process tone-mapping can be applied before
//	 final output.
// - post-process full screen effects are restricted to those which only require
//   color as input.
// - does not handle loss of device.
// - readback is for color only, no depth readback currently.
// - basic Maya material support in a fixed-function pipeline.
// - geometry support for polys for shaded with file texture on color channel.
//
//
#endif

D3DViewportRenderer::D3DViewportRenderer()
:	MViewportRenderer("D3DViewportRenderer")
{
	// Set the ui name
	fUIName.set( "Direct3D Renderer");

	// This renderer overrides all drawing
	fRenderingOverride = MViewportRenderer::kOverrideAllDrawing;

	// Set API and version number
	m_API = MViewportRenderer::kDirect3D;
	m_Version = 9.0f;

	// Default to something reasonable.
	m_renderWidth = 640;
	m_renderHeight = 480;

#if defined(D3D9_SUPPORTED)
	m_hWnd = 0;
	m_pD3D = 0;
	m_pD3DDevice = 0;
	m_pTextureOutput = 0;
	m_pTextureOutputSurface = 0;
	
	m_readBackBuffer.create(m_renderWidth, m_renderHeight, 4/* MPixelType type = kByte */);
	m_readBackBuffer.setRGBA( false );

	m_pBoundsBuffer = 0;
	m_pGeometry = 0;

	m_wantFloatingPointTargets = false;
	m_pTextureInterm = 0;
	m_pTextureIntermSurface = 0;
	m_pTexturePost = 0;

	m_pDepthStencilSurface = 0;
	m_SystemMemorySurface = 0;

	m_requireDepthStencilReadback = false;
#endif
}

/* virtual */
D3DViewportRenderer::~D3DViewportRenderer()
{
	uninitialize();
}

// Dummy window proc.
LRESULT CALLBACK D3DWindowProc( HWND   hWnd, 
							 UINT   msg, 
							 WPARAM wParam, 
							 LPARAM lParam )
{
    switch( msg )
	{
 		case WM_CLOSE:
		{
			//PostQuitMessage(0);	-- can't allow this. Will kill Maya
		}
		break;

        case WM_DESTROY:
		{
            //PostQuitMessage(0);	-- can't allow this. Will kill Maya
		}
        break;
		
		default:
		{
			return DefWindowProc( hWnd, msg, wParam, lParam );
		}
		break;
	}

	return 0;
}

#if defined(D3D9_SUPPORTED)
bool
D3DViewportRenderer::buildRenderTargets(unsigned int width, unsigned int height)
{
	HRESULT hr = -1;

	// Nothing to do, just return
	if (width == m_renderWidth &&
		height == m_renderHeight &&
		m_pTextureInterm && 
		m_pTextureOutput &&
		m_pTexturePost)
	{
		return true;
	}

	// Set the new width and height
	m_renderWidth = width;
	m_renderHeight = height;

	//printf("New size = %d,%d\n", m_renderWidth, m_renderHeight);

	//
	// Create target for intermediate rendering
	//
	if (m_pTextureInterm)
	{
		m_pTextureInterm->Release();
		m_pTextureInterm = NULL;
	}
	if (m_pTextureIntermSurface)
	{
		m_pTextureIntermSurface->Release();
		m_pTextureIntermSurface = NULL;
	}
	if (!m_pTextureInterm)
	{
		hr = D3DXCreateTexture( m_pD3DDevice, 
			m_renderWidth, 
			m_renderHeight, 
			1, 
			D3DUSAGE_RENDERTARGET, 
			m_intermediateTargetFormat, /* Use intermediate target format */
			D3DPOOL_DEFAULT, 
			&m_pTextureInterm );

		// Failed to get target with desired intermediate format. Try for
		// fixed as a default
		m_intermediateTargetFormat = m_outputTargetFormat;
		if ( FAILED(hr) )
		{
			hr = D3DXCreateTexture( m_pD3DDevice, 
				m_renderWidth, 
				m_renderHeight, 
				1, 
				D3DUSAGE_RENDERTARGET, 
				m_intermediateTargetFormat, /* Use output target format */
				D3DPOOL_DEFAULT, 
				&m_pTextureInterm );
		}
		if ( FAILED(hr) )
		{
			MGlobal::displayWarning("Direct3D renderer : Failed to create intermediate texture for offscreen render target.");
			return false;
		}
	}
	if (m_pTextureInterm)
	{
		hr = m_pTextureInterm->GetSurfaceLevel( 0, &m_pTextureIntermSurface );
	}
	if ( FAILED(hr) )
	{
		MGlobal::displayWarning("Direct3D renderer : Failed to get surface for off-screen render target.");
		return false;
	}


	//
	// 2. Create output render targets
	//
	// If we don't want floating point, then the intermediate is
	// the final output format, so don't bother creating another one.
	// Just make the output point to the intermediate target.
	if (m_wantFloatingPointTargets)
	{
		if (m_pTextureOutput)
		{
			m_pTextureOutput->Release();
			m_pTextureOutput = NULL;
		}
		if (m_pTextureOutputSurface)
		{
			m_pTextureOutputSurface->Release();
			m_pTextureOutputSurface = NULL;
		}

		if (!m_pTextureOutput)
		{
			// Create texture for render target
			hr = D3DXCreateTexture( m_pD3DDevice, 
				m_renderWidth, 
				m_renderHeight, 
				1, 
				D3DUSAGE_RENDERTARGET, 
				m_outputTargetFormat, 
				D3DPOOL_DEFAULT, 
				&m_pTextureOutput );

			if ( FAILED(hr) )
			{
				MGlobal::displayWarning("Direct3D renderer : Failed to create texture for offscreen render target.");
				return false;
			}
		}

		// Get the surface (0) for the texture. Could probably do this one
		// per refresh and not keep it around...	
		if (m_pTextureOutput)
		{
			hr = m_pTextureOutput->GetSurfaceLevel( 0, &m_pTextureOutputSurface );
		}
		if ( FAILED(hr) )
		{
			MGlobal::displayWarning("Direct3D renderer : Failed to get surface for off-screen render target.");
			return false;
		}
	}
	else
	{
		m_pTextureOutput = m_pTextureInterm;
		m_pTextureOutputSurface = m_pTextureIntermSurface;
	}

	//
	// 3. Create post-process render targets
	//
	if (m_pTexturePost)
	{
		m_pTexturePost->Release();
		m_pTexturePost = NULL;
	}
	if (!m_pTexturePost)
	{
		hr = D3DXCreateTexture( m_pD3DDevice, 
			m_renderWidth, 
			m_renderHeight, 
			1, 
			D3DUSAGE_RENDERTARGET, 
			m_intermediateTargetFormat, /* Use intermediate target format */
			D3DPOOL_DEFAULT, 
			&m_pTexturePost );

		if ( FAILED(hr) )
		{
			MGlobal::displayWarning("Direct3D renderer : Failed to create texture for offscreen post-processing.");
			return false;
		}
	}

	// 4. Create system memory surface for readback
	if (m_SystemMemorySurface)
	{
		m_SystemMemorySurface->Release();
		m_SystemMemorySurface = 0;
	}
	if (!m_SystemMemorySurface)
	{
		hr = m_pD3DDevice->CreateOffscreenPlainSurface( m_renderWidth,
			m_renderHeight, m_outputTargetFormat, D3DPOOL_SYSTEMMEM,
			&m_SystemMemorySurface, NULL );
		if (FAILED(hr))
		{
			MGlobal::displayWarning("Direct3D renderer : Failed to create system memory readback surface.");
			return false;
		}
	}

	return (m_pTextureOutput && m_pTextureOutputSurface && m_pTextureInterm && 
			m_pTextureIntermSurface && m_pTexturePost && m_SystemMemorySurface);

#if defined(DEPTH_REQUIRED)
	// 5. Create depth stencil surface for access for readback.
	if (m_pDepthStencilSurface)
	{
		m_pDepthStencilSurface->Release();
		m_pDepthStencilSurface = 0;
	}
	if (m_requireDepthStencilReadback && !m_pDepthStencilSurface)
	{
		hr = m_pD3DDevice->CreateDepthStencilSurface( 
						m_renderWidth, m_renderHeight, m_depthStencilFormat, D3DMULTISAMPLE_NONE, 
						0, FALSE, 
						&m_pDepthStencilSurface, NULL );
		if (FAILED(hr))
		{
			MGlobal::displayWarning("Direct3D renderer : Failed to create depth/stencil surface. Depth read back will not be available.");
		}
	}
#endif
}
#endif

/* virtual */	
MStatus	
D3DViewportRenderer::initialize()
{
	MStatus status = MStatus::kFailure;

#if defined(D3D9_SUPPORTED)

	// Do we want floating point targets
	//
	MString wantFloatingPoint("D3D_RENDERER_FLOAT_TARGETS");
	int value;
	if (!MGlobal::getOptionVarValue(wantFloatingPoint, value))
	{
		m_wantFloatingPointTargets = true;
	}
	else
	{
		m_wantFloatingPointTargets = (value != 0);
	}
	m_wantFloatingPointTargets = false;

	// Create the window to contain our off-screen target.
	//
	if (!m_hWnd)
	{
		// Register the window class
		WNDCLASSEX wc = { sizeof(WNDCLASSEX), CS_CLASSDC, (WNDPROC) D3DWindowProc, 0L, 0L, 
                      GetModuleHandle(NULL), NULL, NULL, NULL, NULL,
					  "D3D Viewport Renderer", NULL };
		if (RegisterClassEx( &wc ))
		{
			m_hWnd = CreateWindow( "D3D Viewport Renderer", "D3D Viewport Renderer", 
									WS_OVERLAPPEDWINDOW, 0, 0, m_renderWidth, m_renderHeight,
									NULL, NULL, wc.hInstance, NULL );
		}
	}

	// Startup D3D
	if (m_hWnd)
	{
		if (!m_pD3D)
			m_pD3D = Direct3DCreate9( D3D_SDK_VERSION );
	}

	HRESULT hr;

	// Test for floating point buffer usage for render targets
	if (m_wantFloatingPointTargets)
	{
		m_intermediateTargetFormat = D3DFMT_A16B16G16R16F;
	}
	else
	{
		m_intermediateTargetFormat = D3DFMT_A8R8G8B8;
	}
	// The output target is always fixed8 for now.
	m_outputTargetFormat = D3DFMT_A8R8G8B8;
	if (m_requireDepthStencilReadback)
	{
		m_depthStencilFormat = D3DFMT_D32; // Let's try for 32-bit depth, not stencil
	}
	else
		m_depthStencilFormat = D3DFMT_D24S8; 

	// Create an appropriate device
	if (m_pD3D)
	{
		if (!m_pD3DDevice)
		{
			D3DPRESENT_PARAMETERS d3dpp; 
			ZeroMemory( &d3dpp, sizeof(d3dpp) );

			d3dpp.BackBufferFormat		 = m_outputTargetFormat;
			d3dpp.Windowed				 = TRUE; // Don't want full screen
			d3dpp.BackBufferWidth        = 1920; // Make it big enough to avoid clipping.
			d3dpp.BackBufferHeight       = 1680;
			d3dpp.SwapEffect			 = D3DSWAPEFFECT_DISCARD;
			d3dpp.PresentationInterval   = D3DPRESENT_INTERVAL_IMMEDIATE;
			d3dpp.EnableAutoDepthStencil = TRUE;
			d3dpp.AutoDepthStencilFormat = D3DFMT_D24S8; 

			// Try hardware vertex processing first.
			//
			hr = m_pD3D->CreateDevice( D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, m_hWnd,
										D3DCREATE_HARDWARE_VERTEXPROCESSING | D3DCREATE_FPU_PRESERVE,
										&d3dpp, &m_pD3DDevice );
			
			// Try software if we can't find hardware.
			if (FAILED(hr))
			{
				hr = m_pD3D->CreateDevice( D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, m_hWnd,
										D3DCREATE_SOFTWARE_VERTEXPROCESSING | D3DCREATE_FPU_PRESERVE,
										&d3dpp, &m_pD3DDevice );
			}
			if ( FAILED(hr) )
				m_pD3DDevice = 0;
		}
	}

	// Turn on Z buffer.
	if (m_pD3DDevice)
	{
		m_pD3DDevice->SetRenderState( D3DRS_ZENABLE, TRUE );

		bool builtRenderTargets = buildRenderTargets(640, 480);	
		if (builtRenderTargets)
		{
			MString shaderLocation(MString(getenv("MAYA_LOCATION")) + MString("\\bin\\HLSL"));

			// Load in any post effects from a given directory
			bool loaded = m_resourceManager.initializePostEffects( shaderLocation, m_pD3DDevice );

			// Load in default surface effect from a given directory
			const MString defaultSurfaceEffect("Maya_fixedFunction");
			loaded = m_resourceManager.initializeDefaultSurfaceEffect( shaderLocation, m_pD3DDevice, defaultSurfaceEffect);
			//printf("Loaded Maya fixed function = %d\n", loaded);

			// All elements must exist for success
			if (m_hWnd && m_pD3D && m_pD3DDevice && m_pTextureOutput && m_pTextureOutputSurface && 
				m_pTextureInterm && m_pTextureIntermSurface )
			{
				status = MStatus::kSuccess;
			}
		}
	}

	// If for any reason we failed. Cleanup what we can.
	if (status != MStatus::kSuccess)
	{
		uninitialize();
	}
#else
	status = MStatus::kSuccess;
#endif

	return status;
}

/* virtual */	
MStatus	
D3DViewportRenderer::uninitialize()
{	
#if defined(D3D9_SUPPORTED)
	// 
	// Cleanup D3D items
	//
	if ( m_pTextureOutput )
	{
		if (m_pTextureOutput != m_pTextureInterm)		
			m_pTextureOutput->Release();
		m_pTextureOutput = 0;
	}

	if ( m_pTextureOutputSurface )
	{
		if (m_pTextureOutputSurface != m_pTextureIntermSurface)
			m_pTextureOutputSurface->Release();
		m_pTextureOutputSurface = 0;
	}

	if ( m_pTextureInterm )
	{
        m_pTextureInterm->Release();
		m_pTextureInterm = 0;
	}

	if ( m_pTextureIntermSurface )
	{
        m_pTextureIntermSurface->Release();
		m_pTextureIntermSurface = 0;
	}

	if ( m_pTexturePost )
	{
        m_pTexturePost->Release();
		m_pTexturePost = 0;
	}

	if ( m_SystemMemorySurface )
	{
		m_SystemMemorySurface->Release();
		m_SystemMemorySurface = 0;
	}
	if (m_pDepthStencilSurface)
	{
		m_pDepthStencilSurface->Release();
		m_pDepthStencilSurface = 0;
	}

	if ( m_pBoundsBuffer != NULL )
	{
        m_pBoundsBuffer->Release(); 
		m_pBoundsBuffer = 0;
	}
	if (m_pGeometry)
	{
		m_pGeometry->Release();
		m_pGeometry = 0;
	}
	m_resourceManager.clearResources(false, true); /* wipe out shaders */

    if ( m_pD3DDevice )
	{
        m_pD3DDevice->Release();
		m_pD3DDevice = 0;
	}

    if ( m_pD3D )
	{
        m_pD3D->Release();	
		m_pD3D = 0;
	}

	// Clean up windowing items.
	//
	if (m_hWnd)
	{
		ReleaseDC( m_hWnd, GetDC(m_hWnd ));
		DestroyWindow(m_hWnd);
		UnregisterClass("D3D Viewport Renderer", GetModuleHandle(NULL));
		m_hWnd = 0;
	}

#endif
	return MStatus::kSuccess;
}

/* virtual */ 
MStatus	
D3DViewportRenderer::render(const MRenderingInfo &renderInfo)
{
	MStatus status;

	// Print some diagnostic information.
	//

#if defined(D3D9_SUPPORTED)
	const MRenderTarget & target = renderInfo.renderTarget();
	unsigned int currentWidth = target.width();
	unsigned int currentHeight = target.height();
	
	if (!buildRenderTargets(currentWidth, currentHeight))
		return MStatus::kFailure;
#endif

	//printf("Render using (%s : %s) renderer\n", fName.asChar(), fUIName.asChar());
	//printf("Render region: %d,%d -> %d, %d into target of size %d,%d\n", 
	//	renderInfo.originX(), renderInfo.originY(), renderInfo.width(), renderInfo.height(),
	//	target.width(), target.height() );


#if defined(D3D9_SUPPORTED)
	MViewportRenderer::RenderingAPI targetAPI = renderInfo.renderingAPI();
	//float targetVersion = renderInfo.renderingVersion();
	//printf("Render target API is %s (Version %g)\n", targetAPI == MViewportRenderer::kDirect3D ?
	//		"Direct3D" : "OpenGL", targetVersion);

	// Render if we get a valid camera
	const MDagPath &cameraPath = renderInfo.cameraPath();
	if ( m_resourceManager.translateCamera( cameraPath ) )
	{
		if ( renderToTarget( renderInfo ) )
		{
			// Read back results and set into an intermediate buffer,
			// if the target is not Direct3D. Also readback if we
			// want to debug the buffer.
			//
			bool requireReadBack = (targetAPI != MViewportRenderer::kDirect3D);
			if ( requireReadBack )
			{
				if (readFromTargetToSystemMemory())
				{
					// Blit image back to OpenGL 
					if (targetAPI == MViewportRenderer::kOpenGL)
					{
						// Center the image for now.
						//
						unsigned int targetW = target.width();
						unsigned int targetH = target.height();							
						unsigned int m_readBackBufferWidth, m_readBackBufferHeight;
						m_readBackBuffer.getSize(m_readBackBufferWidth, m_readBackBufferHeight);

						if (m_readBackBufferWidth && m_readBackBufferHeight)
						{
							if (m_readBackBufferWidth > targetW ||
								m_readBackBufferHeight > targetH)
							{
								m_readBackBuffer.resize(targetW, targetH);
								target.writeColorBuffer( m_readBackBuffer, 0, 0 );
							}
							else
							{
								target.writeColorBuffer( m_readBackBuffer, 
									(short)(targetW/2 - m_readBackBufferWidth/2),
									(short)(targetH/2 - m_readBackBufferHeight/2));
							}
							status = MStatus::kSuccess;
						}
					}

					// Blit image back to a software raster
					else
					{
						// To ADD
						status = MStatus::kFailure;
					}
				}
				else
					status = MStatus::kFailure;				
			}

			// Do nothing here. Direct rendering to D3D target
			// should be handled in renderToTarget().
			else 
			{
				status = MStatus::kSuccess;
			}
		}
		else
			status = MStatus::kFailure;
	}
	else
	{
		MGlobal::displayWarning("Direct3D renderer : No valid render camera to use. Nothing rendered\n");
		status = MStatus::kFailure;
	}
#else
		status = MStatus::kSuccess;
#endif
	return status;
}

/* virtual */ 
bool	
D3DViewportRenderer::nativelySupports( MViewportRenderer::RenderingAPI api, 
									   float version )
{
	// Do API and version check
	return ((api == m_API) && (version == m_Version) );
}

/* virtual */ bool	
D3DViewportRenderer::override( MViewportRenderer::RenderingOverride override )
{
	// Check override
	return (override == fRenderingOverride);
}

////////////////////////////////////////////////////////////////////////////////////////////////////
// Rendering methods
////////////////////////////////////////////////////////////////////////////////////////////////////
#if defined(D3D9_SUPPORTED)

bool					
D3DViewportRenderer::translateCamera( const MRenderingInfo &renderInfo )
//
// Description:
//		Translate Maya's camera 
//
{
	const MDagPath &cameraPath = renderInfo.cameraPath();
	if (cameraPath.isValid())
		return m_resourceManager.translateCamera( cameraPath );
	else
		return false;
}

bool D3DViewportRenderer::drawCube(float minVal[3], float maxVal[3], bool filled, bool useDummyGeometry,
								   float color[3], LPDIRECT3DVERTEXBUFFER9 buffer /* = NULL */)
//
// Description:
//		Draw a rectangular bounds of min->max size.
//
{
	if (!m_pD3DDevice)
		return false;

	D3DMATERIAL9 Material;
	Material.Emissive.r = color[0]; 
	Material.Emissive.g = color[1]; 
	Material.Emissive.b = color[2]; 
	Material.Emissive.a = 1.0f; 
	Material.Ambient.r = color[0]; 
	Material.Ambient.g = color[1]; 
	Material.Ambient.b = color[2]; 
	Material.Ambient.a = 1.0f; 
	m_pD3DDevice->SetMaterial( &Material);
	m_pD3DDevice->LightEnable( 0, false);
	m_pD3DDevice->LightEnable( 1, false);
	m_pD3DDevice->SetRenderState(D3DRS_ALPHABLENDENABLE, false);

	// Set up render state
	//
	if (!filled)
	{
		m_pD3DDevice->SetRenderState( D3DRS_FILLMODE, D3DFILL_WIREFRAME );
	}
	else
	{
		m_pD3DDevice->SetRenderState( D3DRS_FILLMODE, D3DFILL_SOLID );		
	}
	m_pD3DDevice->SetRenderState( D3DRS_CULLMODE, D3DCULL_NONE );
	m_pD3DDevice->SetRenderState( D3DRS_SHADEMODE, D3DSHADE_FLAT );

	// 
	if (!m_pGeometry)
	{
		if (useDummyGeometry)
		{
			//D3DXCreateSphere(m_pD3DDevice, 1.0f, 20, 20, &m_pGeometry, NULL);
			//D3DXCreateTeapot(m_pD3DDevice, &m_pGeometry, NULL);
			//D3DXCreateBox(m_pD3DDevice, 1.0, 1.0, 1.0, &m_pGeometry, NULL);
		}
	}
	if (useDummyGeometry && m_pGeometry)
	{
		m_pGeometry->DrawSubset(0);
		return true;
	}

	// Build a vertex buffer to hold a cube once.
	//
	LPDIRECT3DVERTEXBUFFER9 bufferToFill = m_pBoundsBuffer;
	if (!bufferToFill)
	{
		m_pD3DDevice->CreateVertexBuffer( 24*sizeof(PlainVertex),0, PlainVertex::FVF_Flags,
											D3DPOOL_DEFAULT, &bufferToFill, NULL );
	}
	if (!bufferToFill)
	{
		return false;
	}
	
	PlainVertex cube[] =
	{
		{ minVal[0], maxVal[1], minVal[2]},
		{ maxVal[0], maxVal[1], minVal[2]},
		{ minVal[0], minVal[1], minVal[2] },
		{ maxVal[0], minVal[1], minVal[2] },

		{minVal[0], maxVal[1], maxVal[2] },
		{minVal[0],minVal[1], maxVal[2] },
		{ maxVal[0], maxVal[1], maxVal[2] },
		{ maxVal[0],minVal[1], maxVal[2] },

		{minVal[0], maxVal[1], maxVal[2] },
		{ maxVal[0], maxVal[1], maxVal[2] },
		{minVal[0], maxVal[1],minVal[2] },
		{ maxVal[0], maxVal[1],minVal[2] },

		{minVal[0],minVal[1], maxVal[2] },
		{minVal[0],minVal[1],minVal[2] },
		{ maxVal[0],minVal[1], maxVal[2] },
		{ maxVal[0],minVal[1],minVal[2] },

		{ maxVal[0], maxVal[1],minVal[2] },
		{ maxVal[0], maxVal[1], maxVal[2] },
		{ maxVal[0],minVal[1],minVal[2] },
		{ maxVal[0],minVal[1], maxVal[2] },

		{minVal[0], maxVal[1],minVal[2] },
		{minVal[0],minVal[1],minVal[2] },
		{minVal[0], maxVal[1], maxVal[2] },
		{minVal[0],minVal[1], maxVal[2] }
	};

	void *pVertices = NULL;

	// Do this everytime as we don't store more than one box,
	// and we don't check for changes in bounds sizes.
	//
	bufferToFill->Lock( 0, sizeof(cube), (void**)&pVertices, 0 );
	memcpy( pVertices, cube, sizeof(cube) );
	bufferToFill->Unlock();

    m_pD3DDevice->SetStreamSource( 0, bufferToFill, 0, sizeof(PlainVertex) );
    m_pD3DDevice->SetFVF( PlainVertex::FVF_Flags );


	m_pD3DDevice->DrawPrimitive( D3DPT_TRIANGLESTRIP,  0, 2 );
	m_pD3DDevice->DrawPrimitive( D3DPT_TRIANGLESTRIP,  4, 2 );
	m_pD3DDevice->DrawPrimitive( D3DPT_TRIANGLESTRIP,  8, 2 );
	m_pD3DDevice->DrawPrimitive( D3DPT_TRIANGLESTRIP, 12, 2 );
	m_pD3DDevice->DrawPrimitive( D3DPT_TRIANGLESTRIP, 16, 2 );
	m_pD3DDevice->DrawPrimitive( D3DPT_TRIANGLESTRIP, 20, 2 );

	m_pD3DDevice->LightEnable( 0, true);
	m_pD3DDevice->LightEnable( 1, true);

	return true;
}

void					
D3DViewportRenderer::clearResources(bool onlyInvalidItems, bool clearShaders)
{
	m_resourceManager.clearResources( onlyInvalidItems, clearShaders );
}

MObject findShader( MObject& setNode )
//
//  Description:
//      Find the shading node for the given shading group set node.
//
{
	MFnDependencyNode fnNode(setNode);
	MPlug shaderPlug = fnNode.findPlug("surfaceShader");
			
	if (!shaderPlug.isNull()) {			
		MPlugArray connectedPlugs;
		bool asSrc = false;
		bool asDst = true;
		shaderPlug.connectedTo( connectedPlugs, asDst, asSrc );

		if (connectedPlugs.length() != 1)
			MGlobal::displayError("Error getting shader");
		else 
			return connectedPlugs[0].node();
	}			
	
	return MObject::kNullObj;
}

bool D3DViewportRenderer::setSurfaceMaterialShader( const MDagPath &dagPath, D3DGeometry* Geometry,
												   	const D3DXMATRIXA16 &objectToWorld, 
												   	const D3DXMATRIXA16 &objectToWorldInvTranspose, 
													const D3DXMATRIXA16 &worldViewProjection,
													const D3DXVECTOR4 &worldEyePosition)
{
	if (!Geometry)
		return false;

	const SurfaceEffectItemList & surfaceEffects = m_resourceManager.getSurfaceEffectItemList();
	bool havePixelShader = (surfaceEffects.size() > 0);
	if (havePixelShader)
	{
		// Default is always the first shader !
		SurfaceEffectItem *defaultItem = surfaceEffects.front();
		if (defaultItem)
		{
			ID3DXEffect* effect = defaultItem->fEffect;
			if (effect)
			{
				//printf("Setup effects technique...\n");
				HRESULT hres = effect->SetTechnique("MayaPhong");
				if (hres != D3D_OK)
					havePixelShader = false;
				else
				{
					//printf("Set up effect parameters\n");
					D3DXHANDLE handle;
					handle = effect->GetParameterBySemantic( NULL, "WorldView" );
					effect->SetMatrix( handle, &objectToWorld );
					handle = effect->GetParameterBySemantic( NULL, "WorldViewInverseTranspose" );
					effect->SetMatrix( handle, &objectToWorldInvTranspose );
					handle = effect->GetParameterBySemantic( NULL, "WorldViewProjection" );
					effect->SetMatrix( handle, &worldViewProjection );

					// Required for specular lighting.
					//effect->SetVector( "worldEyePosition", &worldEyePosition );

					// Setup lighting parameters
					MItDag dagIterator( MItDag::kDepthFirst, MFn::kLight );
					MDagPath lightPath;
					for (; !dagIterator.isDone(); dagIterator.next())
					{
						if ( !dagIterator.getPath(lightPath) )
							continue;
						MFnLight    fnLight( lightPath );
						MTransformationMatrix worldMatrix = lightPath.inclusiveMatrix();

						MVector translation = worldMatrix.translation( MSpace::kWorld );
						MVector direction( 0.0, 0.0, 1.0 ); 
						direction *= worldMatrix.asMatrix();
						direction.normalize();

						// Hard coded for directional only currently
						D3DXVECTOR4 e_val((float)direction.x, (float)direction.y, (float)direction.z, 1.0f );
						hres = effect->SetVector( "lightDir", &e_val );

						MColor      colorVal = fnLight.color();
						float intensity = fnLight.intensity();

						D3DXVECTOR4 c_val( colorVal.r * intensity, colorVal.g * intensity, colorVal.b * intensity, 1.0f );
						hres = effect->SetVector( "lightColor", &c_val );
					}
	
					// Setup material parameters
					//
					bool isTransparent = false;
					MFnMesh fnMesh(dagPath);
					MObjectArray sets;
					MObjectArray comps;
					unsigned int instanceNum = dagPath.instanceNumber();
					if (!fnMesh.getConnectedSetsAndMembers(instanceNum, sets, comps, true))
						MGlobal::displayError("ERROR: MFnMesh::getConnectedSetsAndMembers");
					if (sets.length())
					{
						MObject set = sets[0];
						MObject comp = comps[0];

						MStatus status;
						MObject shaderNode = findShader(set);
						if (shaderNode != MObject::kNullObj)
						{								
							float rgb[3];

							MPlug colorPlug = MFnDependencyNode(shaderNode).findPlug("color", &status);
							D3DTexture* Texture = NULL;
							if (status != MS::kFailure)
							{
								MItDependencyGraph It( colorPlug, MFn::kFileTexture, MItDependencyGraph::kUpstream);
								if( !It.isDone())
								{
									Texture = m_resourceManager.getTexture( It.thisNode());
									hres = effect->SetTexture( "diffuseTexture", Texture->Texture( m_pD3DDevice ));
									D3DXVECTOR4 e_val(1.0f, 1.0f, 1.0f, 1.0f );
									hres = effect->SetVector( "diffuseMaterial", &e_val);

									// Change the technique to a textured one
									hres = effect->SetTechnique("MayaPhongTextured");
								}
								else
								{
									MObject data;
									colorPlug.getValue( data);
									MFnNumericData val(data);
									val.getData( rgb[0], rgb[1], rgb[2]);
									
									D3DXVECTOR4 e_val(rgb[0], rgb[1], rgb[2], 1.0f );
									hres = effect->SetVector( "diffuseMaterial", &e_val);
								}
							}

							MPlug diffusePlug = MFnDependencyNode(shaderNode).findPlug("diffuse", &status);
							if (status != MS::kFailure)
							{
								MObject data;
								float diff;
								diffusePlug.getValue( diff );
								//Material.Diffuse.r *= (float)diff;
								//Material.Diffuse.g *= (float)diff; 
								//Material.Diffuse.b *= (float)diff;
							}
							MPlug ambientColorPlug = MFnDependencyNode(shaderNode).findPlug("ambientColor", &status);
							if (status != MS::kFailure)
							{
								MObject data;
								ambientColorPlug .getValue( data);
								MFnNumericData val(data);
								val.getData( rgb[0], rgb[1], rgb[2]);

								D3DXVECTOR4 e_val( rgb[0], rgb[1], rgb[2], 1.0f );
								effect->SetVector( "ambientMaterial", &e_val );
							}
							MPlug transparencyPlug = MFnDependencyNode(shaderNode).findPlug("transparency", &status);
							if (status != MS::kFailure)
							{
								MObject data;
								transparencyPlug.getValue( data);
								MFnNumericData val(data);
								val.getData( rgb[0], rgb[1], rgb[2]);

								D3DXVECTOR4 e_val( 1.0f - rgb[0], 1.0f - rgb[1], 1.0f - rgb[2], 1.0f );
								effect->SetVector( "transparency", &e_val );
								if (rgb[0] < 1.0f || rgb[1] < 1.0f || rgb[2] < 1.0f)
									isTransparent = true;
							}
							MPlug incandescencePlug = MFnDependencyNode(shaderNode).findPlug("incandescence", &status);
							if (status != MS::kFailure)
							{
								MObject data;
								incandescencePlug.getValue( data);
								MFnNumericData val(data);
								val.getData( rgb[0], rgb[1], rgb[2]);
								//Material.Emissive.r = (float)rgb[0]; 
								//Material.Emissive.g = (float)rgb[1]; 
								//Material.Emissive.b = (float)rgb[2]; Material.Emissive.a = 1.0f; 
							}
							MPlug specularColorPlug = MFnDependencyNode(shaderNode).findPlug("specularColor", &status);
							if (status != MS::kFailure)
							{
								MObject data;
								specularColorPlug.getValue( data);
								MFnNumericData val(data);
								val.getData( rgb[0], rgb[1], rgb[2]);

								D3DXVECTOR4 e_val( rgb[0], rgb[1], rgb[2], 1.0f );
								effect->SetVector( "specularMaterial", &e_val );
							}						
							// Rough approximations for Phong, PhongE, and Blinn.
							//
							if (shaderNode.hasFn(MFn::kLambert))
							{
								effect->SetFloat( "specularPower", 0.0f );
							}
							if (shaderNode.hasFn(MFn::kPhong))
							{
								MPlug cosinePowerPlug = MFnDependencyNode(shaderNode).findPlug("cosinePower", &status);
								if (status != MS::kFailure)
								{
									MObject data;
									float cosPower = 0.0f;
									cosinePowerPlug.getValue( cosPower );
									hres = effect->SetFloat( "specularPower", cosPower * 4.0f );
								}						
							}
							else if (MFn::kBlinn)
							{
								MPlug eccentricityPlug = MFnDependencyNode(shaderNode).findPlug("eccentricity", &status);
								if (status != MS::kFailure)
								{
									// Maya's funky remapping of eccentricity into cosinePower.
									//
									MObject data;
									float eccentricity = 0.0f;
									eccentricityPlug.getValue( eccentricity );
									hres = effect->SetFloat( "specularPower", (eccentricity < 0.03125f) ? 128.0f : 4.0f / eccentricity );
								}
							}
							else // if (shaderNode.hasFn(MFn::kPhongE))
							{
								MPlug roughnessPlug = MFnDependencyNode(shaderNode).findPlug("roughness", &status);
								if (status != MS::kFailure)
								{
									MObject data;
									float roughness = 0.0f;
									roughnessPlug.getValue( roughness );
									hres = effect->SetFloat( "specularPower", roughness * 4.0f );
								}
							}
						}
					}

					unsigned int numPasses;
					effect->Begin( &numPasses, 0 );
					if (hres != D3D_OK)
						havePixelShader = false;
					else
					{
						//printf("Draw %d passes\n", numPasses);
						for (unsigned int p=0; p<numPasses; ++p)
						{
							hres = effect->BeginPass( p );
							if (hres == D3D_OK) 
							{
								// Enable transparency if required by material.
								//
								//m_pD3DDevice->SetRenderState(D3DRS_ALPHABLENDENABLE, isTransparent);
								//m_pD3DDevice->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_SRCALPHA);
								//m_pD3DDevice->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA);

								//printf("Draw surface shader pass\n");
								Geometry->Render( m_pD3DDevice);
							}
							hres = effect->EndPass();
						}
					}
					hres = effect->End();
					if (hres != D3D_OK) 
						havePixelShader = false;
				}
			}
		}
	}

	return havePixelShader;
}


bool D3DViewportRenderer::setSurfaceMaterial( const MDagPath &dagPath )
{
	D3DMATERIAL9 Material;
	bool isTransparent = false;

	MFnMesh fnMesh(dagPath);
	MObjectArray sets;
	MObjectArray comps;
	unsigned int instanceNum = dagPath.instanceNumber();
	if (!fnMesh.getConnectedSetsAndMembers(instanceNum, sets, comps, true))
		MGlobal::displayError("ERROR : MFnMesh::getConnectedSetsAndMembers");
	for ( unsigned i=0; i<sets.length(); i++ ) 
	{
		MObject set = sets[i];
		MObject comp = comps[i];

		MStatus status;
		MFnSet fnSet( set, &status );
		if (status == MS::kFailure) {
			MGlobal::displayError("ERROR: MFnSet::MFnSet");
			continue;
		}

		MObject shaderNode = findShader(set);
		if (shaderNode != MObject::kNullObj)
		{								
			float rgb[3];

			MPlug colorPlug = MFnDependencyNode(shaderNode).findPlug("color", &status);
			D3DTexture* Texture = NULL;
			if (status != MS::kFailure)
			{
				MItDependencyGraph It( colorPlug, MFn::kFileTexture, MItDependencyGraph::kUpstream);
				if( !It.isDone())
				{
					Texture = m_resourceManager.getTexture( It.thisNode());
					m_pD3DDevice->SetTexture( 0, Texture->Texture( m_pD3DDevice));
					m_pD3DDevice->SetTextureStageState(0, D3DTSS_COLOROP, D3DTOP_MODULATE);
					m_pD3DDevice->SetTextureStageState(0, D3DTSS_ALPHAOP, D3DTOP_MODULATE);
					m_pD3DDevice->SetSamplerState( 0, D3DSAMP_MINFILTER, D3DTEXF_LINEAR);
					m_pD3DDevice->SetSamplerState( 0, D3DSAMP_MAGFILTER, D3DTEXF_LINEAR);
					m_pD3DDevice->SetSamplerState( 0, D3DSAMP_MIPFILTER, D3DTEXF_LINEAR);
					m_pD3DDevice->SetRenderState(D3DRS_WRAP0, D3DWRAPCOORD_0);
					m_pD3DDevice->SetRenderState(D3DRS_WRAP1, D3DWRAPCOORD_1);
					Material.Diffuse.r = Material.Diffuse.g = Material.Diffuse.b = Material.Diffuse.a = 1.0f; 
				}
				else
				{
					m_pD3DDevice->SetTexture( 0, NULL);
					MObject data;
					colorPlug.getValue( data);
					MFnNumericData val(data);
					val.getData( rgb[0], rgb[1], rgb[2]);
					Material.Diffuse.r = (float)rgb[0]; 
					Material.Diffuse.g = (float)rgb[1]; 
					Material.Diffuse.b = (float)rgb[2]; Material.Diffuse.a = 1.0f; 
				}
			}

			MPlug diffusePlug = MFnDependencyNode(shaderNode).findPlug("diffuse", &status);
			if (status != MS::kFailure)
			{
				MObject data;
				float diff;
				diffusePlug.getValue( diff );
				Material.Diffuse.r *= (float)diff;
				Material.Diffuse.g *= (float)diff; 
				Material.Diffuse.b *= (float)diff;
			}
			MPlug ambientColorPlug = MFnDependencyNode(shaderNode).findPlug("ambientColor", &status);
			if (status != MS::kFailure)
			{
				MObject data;
				ambientColorPlug .getValue( data);
				MFnNumericData val(data);
				val.getData( rgb[0], rgb[1], rgb[2]);
				Material.Ambient.r = (float)rgb[0]; 
				Material.Ambient.g = (float)rgb[1]; 
				Material.Ambient.b = (float)rgb[2]; Material.Ambient.a = 1.0f; 
			}
			MPlug transparencyPlug = MFnDependencyNode(shaderNode).findPlug("transparency", &status);
			if (status != MS::kFailure)
			{
				MObject data;
				transparencyPlug.getValue( data);
				MFnNumericData val(data);
				val.getData( rgb[0], rgb[1], rgb[2]);
				Material.Diffuse.a = 1.0f - (rgb[0] + rgb[1] + rgb[2]) / 3.0f;
				if (Material.Diffuse.a < 1.0f)
					isTransparent = true;
			}
			MPlug incandescencePlug = MFnDependencyNode(shaderNode).findPlug("incandescence", &status);
			if (status != MS::kFailure)
			{
				MObject data;
				incandescencePlug.getValue( data);
				MFnNumericData val(data);
				val.getData( rgb[0], rgb[1], rgb[2]);
				Material.Emissive.r = (float)rgb[0]; 
				Material.Emissive.g = (float)rgb[1]; 
				Material.Emissive.b = (float)rgb[2]; Material.Emissive.a = 1.0f; 
			}
			MPlug specularColorPlug = MFnDependencyNode(shaderNode).findPlug("specularColor", &status);
			if (status != MS::kFailure)
			{
				MObject data;
				specularColorPlug.getValue( data);
				MFnNumericData val(data);
				val.getData( rgb[0], rgb[1], rgb[2]);
				Material.Specular.r = (float)rgb[0]; 
				Material.Specular.g = (float)rgb[1]; 
				Material.Specular.b = (float)rgb[2]; Material.Specular.a = 1.0f; 
			}						
			// Rough approximations for Phong, PhongE, and Blinn.
			//
			Material.Power = 20.0f;
			if (shaderNode.hasFn(MFn::kLambert))
			{
				Material.Power = 0.0f;
			}
			if (shaderNode.hasFn(MFn::kPhong))
			{
				MPlug cosinePowerPlug = MFnDependencyNode(shaderNode).findPlug("cosinePower", &status);
				if (status != MS::kFailure)
				{
					MObject data;
					float cosPower = 0.0f;
					cosinePowerPlug.getValue( cosPower );
					Material.Power = cosPower * 4.0f;
				}						
			}
			else if (MFn::kBlinn)
			{
				MPlug eccentricityPlug = MFnDependencyNode(shaderNode).findPlug("eccentricity", &status);
				if (status != MS::kFailure)
				{
					// Maya's funky remapping of eccentricity into cosinePower.
					//
					MObject data;
					float eccentricity = 0.0f;
					eccentricityPlug.getValue( eccentricity );
					Material.Power = (eccentricity < 0.03125f) ? 128.0f : 4.0f / eccentricity;
				}
			}
			else // if (shaderNode.hasFn(MFn::kPhongE))
			{
				MPlug roughnessPlug = MFnDependencyNode(shaderNode).findPlug("roughness", &status);
				if (status != MS::kFailure)
				{
					MObject data;
					float roughness = 0.0f;
					roughnessPlug.getValue( roughness );
					Material.Power = roughness * 4.0f;
				}
			}
			break;
		}
	}

	// Enable transparency if required by material.
	//
	m_pD3DDevice->SetRenderState(D3DRS_ALPHABLENDENABLE, isTransparent);
	m_pD3DDevice->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_SRCALPHA);
	m_pD3DDevice->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA);

	m_pD3DDevice->SetMaterial( &Material);

	return true;
}

bool D3DViewportRenderer::drawSurface( const MDagPath &dagPath, bool active, bool templated)
{
	bool drewSurface = false;

	if ( !dagPath.hasFn( MFn::kMesh ))
	{
		MMatrix  matrix = dagPath.inclusiveMatrix();
		MFnDagNode dagNode(dagPath);
		MBoundingBox box = dagNode.boundingBox();
		float color[3] = {0.6f, 0.3f, 0.0f};
		if (active)
		{
			color[0] = 1.0f;
			color[1] = 1.0f;
			color[2] = 1.0f;
		}
		else if (templated)
		{
			color[0] = 1.0f;
			color[1] = 0.686f;
			color[2] = 0.686f;
		}
		drawBounds( matrix, box, false, true, color);
		return true;
	}

	if ( dagPath.hasFn( MFn::kMesh ))
	{
		MMatrix  matrix = dagPath.inclusiveMatrix();
		MFnDagNode dagNode(dagPath);

		// Look for any hardware shaders which can draw D3D first.
		//
		bool drewWithHwShader = false;
		{
			MFnMesh fnMesh(dagPath);
			MObjectArray sets;
			MObjectArray comps;
			unsigned int instanceNum = dagPath.instanceNumber();
			if (!fnMesh.getConnectedSetsAndMembers(instanceNum, sets, comps, true))
				MGlobal::displayError("ERROR : MFnMesh::getConnectedSetsAndMembers");
			for ( unsigned i=0; i<sets.length(); i++ ) 
			{
				MObject set = sets[i];
				MObject comp = comps[i];

				MStatus status;
				MFnSet fnSet( set, &status );
				if (status == MS::kFailure) {
					MGlobal::displayError("ERROR: MFnSet::MFnSet");
					continue;
				}

				MObject shaderNode = findShader(set);
				if (shaderNode != MObject::kNullObj)
				{
					MPxHardwareShader * hwShader = 
						MPxHardwareShader::getHardwareShaderPtr( shaderNode );

					if (hwShader)
					{
						const MRenderProfile & profile = hwShader->profile();
						if (profile.hasRenderer( MRenderProfile::kMayaD3D))
						{
							// Render a Maya D3D hw shader here....
							//printf("Found a D3D hw shader\n");
							//drewWithHwShader = true;
						}
					}
				}
			}
		}

		// Get the geometry buffers for this bad boy and render them
		D3DGeometry* Geometry = m_resourceManager.getGeometry( dagPath, m_pD3DDevice);
		if( Geometry)
		{
			// Transform from object to world space
			//
			D3DXMATRIXA16 objectToWorld = D3DXMATRIXA16
				(
				(float)matrix.matrix[0][0], (float)matrix.matrix[0][1], (float)matrix.matrix[0][2], (float)matrix.matrix[0][3],
				(float)matrix.matrix[1][0], (float)matrix.matrix[1][1], (float)matrix.matrix[1][2], (float)matrix.matrix[1][3],
				(float)matrix.matrix[2][0], (float)matrix.matrix[2][1], (float)matrix.matrix[2][2], (float)matrix.matrix[2][3],
				(float)matrix.matrix[3][0], (float)matrix.matrix[3][1], (float)matrix.matrix[3][2], (float)matrix.matrix[3][3]
			);

			m_pD3DDevice->SetTransform( D3DTS_WORLD, &objectToWorld );
			m_pD3DDevice->SetRenderState( D3DRS_CULLMODE, D3DCULL_NONE );
			m_pD3DDevice->SetRenderState( D3DRS_SHADEMODE, D3DSHADE_GOURAUD );
			m_pD3DDevice->SetRenderState( D3DRS_AMBIENT, 0xFFFFFFFF );
			m_pD3DDevice->SetRenderState( D3DRS_VERTEXBLEND, FALSE );
			m_pD3DDevice->SetRenderState( D3DRS_INDEXEDVERTEXBLENDENABLE, FALSE );
			m_pD3DDevice->SetRenderState( D3DRS_COLORWRITEENABLE, 0xFFFFFFFF );

			if (!drewWithHwShader)
			{
				// Get material properties for shader associated with mesh
				//
				// 1. Try to draw with the sample internal programmable shader
				bool drewGeometryWithShader = false;
				// optionVar -iv "D3D_USE_PIXEL_SHADER" 1; // Set it
				// optionVar -remove "D3D_USE_PIXEL_SHADER"; // Remove it
				MString usePixelShader("D3D_USE_PIXEL_SHADER");
				int val = 0;
				if (MGlobal::getOptionVarValue(usePixelShader, val))
				{
					MMatrix  itmatrix = matrix.inverse().transpose();
					D3DXMATRIXA16 objectToWorldInvTransp = D3DXMATRIXA16
						(
						(float)itmatrix.matrix[0][0], (float)itmatrix.matrix[0][1], (float)itmatrix.matrix[0][2], (float)itmatrix.matrix[0][3],
						(float)itmatrix.matrix[1][0], (float)itmatrix.matrix[1][1], (float)itmatrix.matrix[1][2], (float)itmatrix.matrix[1][3],
						(float)itmatrix.matrix[2][0], (float)itmatrix.matrix[2][1], (float)itmatrix.matrix[2][2], (float)itmatrix.matrix[2][3],
						(float)itmatrix.matrix[3][0], (float)itmatrix.matrix[3][1], (float)itmatrix.matrix[3][2], (float)itmatrix.matrix[3][3]
					);

					MVector eyePos( 0.0, 0.0, 0.0 );
					eyePos *= mm_currentViewMatrix;
					D3DXVECTOR4 worldEye( (float)eyePos[0], (float)eyePos[1], (float)eyePos[2], (float)eyePos[3]);

					drewGeometryWithShader = setSurfaceMaterialShader( dagPath, Geometry, objectToWorld, objectToWorldInvTransp,
						objectToWorld * m_currentViewMatrix * m_currentProjectionMatrix,
						worldEye );
				}

				// 2. Draw with fixed function shader
				if (!drewGeometryWithShader)
				{
					// Set up a default material, just in case there is none.
					//
					D3DMATERIAL9 Material;
					if (active)
					{
						m_pD3DDevice->SetRenderState( D3DRS_FILLMODE, D3DFILL_SOLID );
						Material.Diffuse.r = 1.0f; Material.Diffuse.g = 1.0f; Material.Diffuse.b = 1.0f; Material.Diffuse.a = 1.0f; 
					}
					else if (templated)
					{
						m_pD3DDevice->SetRenderState( D3DRS_FILLMODE, D3DFILL_WIREFRAME );
						Material.Diffuse.r = 1.0f; Material.Diffuse.g = 0.686f; Material.Diffuse.b = 0.686f; Material.Diffuse.a = 1.0f; 
					}
					else
					{
						m_pD3DDevice->SetRenderState( D3DRS_FILLMODE, D3DFILL_SOLID );
						Material.Diffuse.r = 0.5f; Material.Diffuse.g = 0.5f; Material.Diffuse.b = 0.5f; Material.Diffuse.a = 1.0f; 
					}
					Material.Ambient.r = 0.0f; Material.Ambient.g = 0.0f; Material.Ambient.b = 0.0f; Material.Ambient.a = 0.0f; 
					Material.Specular.r = 1.0f; Material.Specular.g = 1.0f; Material.Specular.b = 1.0f; Material.Specular.a = 1.0f; 
					Material.Emissive.r = 0.0f; Material.Emissive.g = 0.0f; Material.Emissive.b = 0.0f; Material.Emissive.a = 0.0f; 
					Material.Power = 50.0f;

					bool scanForMayaMaterial = true;
					if (!templated && scanForMayaMaterial)
					{
						if (!setSurfaceMaterial( dagPath ))
						{
							m_pD3DDevice->SetRenderState(D3DRS_ALPHABLENDENABLE, false);
							m_pD3DDevice->SetMaterial( &Material);
						}
					}

					Geometry->Render( m_pD3DDevice);
				}
			}

			// Draw wireframe on top
			//
			m_pD3DDevice->SetTexture( 0, NULL);
			m_pD3DDevice->SetRenderState(D3DRS_WRAP0, 0);
			m_pD3DDevice->SetRenderState(D3DRS_WRAP1, 0);

			if (active)
			{
				bool drawActiveWithBounds = false;
				if (drawActiveWithBounds)
				{
					MBoundingBox box = dagNode.boundingBox();
					float color[3] = {1.0f, 1.0f, 1.0f};
					drawBounds( matrix, box, false, false, color );
				}
				else
				{
					D3DMATERIAL9 Material;
					Material.Emissive.r = 1.0; 
					Material.Emissive.g = 1.0; 
					Material.Emissive.b = 1.0; 
					Material.Emissive.a = 1.0f; 
					Material.Ambient.r = 1.0; 
					Material.Ambient.g = 1.0; 
					Material.Ambient.b = 1.0; 
					Material.Ambient.a = 1.0f; 
					m_pD3DDevice->SetMaterial( &Material);
					m_resourceManager.enableLights( FALSE, m_pD3DDevice );
					m_pD3DDevice->SetRenderState( D3DRS_ANTIALIASEDLINEENABLE, TRUE );
					m_pD3DDevice->SetRenderState( D3DRS_SHADEMODE, D3DSHADE_FLAT );
					m_pD3DDevice->SetRenderState(D3DRS_ALPHABLENDENABLE, false);
					m_pD3DDevice->SetRenderState( D3DRS_FILLMODE, D3DFILL_WIREFRAME );
					m_pD3DDevice->SetRenderState( D3DRS_SLOPESCALEDEPTHBIAS, 100 );
					m_pD3DDevice->SetRenderState( D3DRS_DEPTHBIAS, 10 );

					Geometry->Render( m_pD3DDevice);				

					m_pD3DDevice->SetRenderState( D3DRS_DEPTHBIAS, 0 );
					m_pD3DDevice->SetRenderState( D3DRS_SLOPESCALEDEPTHBIAS, 0 );
					m_pD3DDevice->SetRenderState( D3DRS_FILLMODE, D3DFILL_SOLID );		
					m_resourceManager.enableLights( TRUE, m_pD3DDevice );
				}
			}
		} // If Geometry
	}
	return drewSurface;
}

bool D3DViewportRenderer::drawScene(const MRenderingInfo &renderInfo)
//
// Description:
//		Draw the Maya scene, using a custom traverser.
//
{
	bool useDrawTraversal = true;
	float groundPlaneColor[3] = { 0.8f, 0.8f, 0.8f };

	if (useDrawTraversal)
	{
		const MDagPath &cameraPath = renderInfo.cameraPath();
		if (cameraPath.isValid())
		{
			// You can actually keep the traverser classes around
			// if desired. Here we just create temporary traversers
			// on the fly.
			//
			MDrawTraversal *trav = new MDrawTraversal;
			trav->enableFiltering( false );
			if (!trav)
			{
				MGlobal::displayWarning("Direct3D renderer : failed to create a traversal class !\n");
				return true;
			}

			const MRenderTarget &renderTarget = renderInfo.renderTarget();
			trav->setFrustum( cameraPath, renderTarget.width(), 
							  renderTarget.height() );

			if (!trav->frustumValid())
			{
				MGlobal::displayWarning("Direct3D renderer : Frustum is invalid !\n");
				return true;
			}

			trav->traverse();

			unsigned int numItems = trav->numberOfItems();
			unsigned int i;
			for (i=0; i<numItems; i++)
			{
				MDagPath path;
				trav->itemPath(i, path);

				if (path.isValid())
				{
					bool drawIt = false;

					// Default traverer may have view manips showing up.
					// This is currently a known Maya bug.
					if ( path.hasFn( MFn::kViewManip ))
						continue;

					//
					// Draw surfaces (polys, nurbs, subdivs)
					//
					bool active = false;
					bool templated = false;
					if ( path.hasFn( MFn::kMesh) || 
						 path.hasFn( MFn::kNurbsSurface) || 
						 path.hasFn( MFn::kSubdiv) )
					{
						drawIt = true;
						if (trav->itemHasStatus( i, MDrawTraversal::kActiveItem ))
						{
							active = true;
						}
						else if (trav->itemHasStatus( i, MDrawTraversal::kTemplateItem ))
						{
							templated = true;
						}
						else
						{
							if (path.hasFn( MFn::kMesh ))
								;
							else if (path.hasFn( MFn::kNurbsSurface))
								;
							else
								;
						}
					}

					//
					// Draw the ground plane
					//
					else if (path.hasFn( MFn::kSketchPlane ) ||
							 path.hasFn( MFn::kGroundPlane ))
					{
						MMatrix  matrix = path.inclusiveMatrix();
						MFnDagNode dagNode(path);
						MBoundingBox box = dagNode.boundingBox();
						drawBounds( matrix, box, false, false, groundPlaneColor );
					}

					if (drawIt)
					{
						drawSurface( path, active, templated );
					}
				}
			}

			if (trav)
				delete trav;

			// Cleanup any unused resource items
			bool onlyInvalidItems = true;
			clearResources( onlyInvalidItems, false );
		}
	}
	else
	{
		// Draw some poly bounding boxes 
		//
		MItDag::TraversalType traversalType = MItDag::kDepthFirst;
		MFn::Type filter = MFn::kMesh;
		MStatus status;

		MItDag dagIterator( traversalType, filter, &status);

		for ( ; !dagIterator.isDone(); dagIterator.next() ) 
		{

			MDagPath dagPath;

			status = dagIterator.getPath(dagPath);
			if ( !status ) {
				status.perror("MItDag::getPath");
				continue;
			}

			MFnDagNode dagNode(dagPath, &status);
			if ( !status ) {
				status.perror("MFnDagNode constructor");
				continue;
			}

			MMatrix  matrix = dagPath.inclusiveMatrix();
			MBoundingBox box = dagNode.boundingBox();
			drawBounds( matrix, box, false, false, NULL );
		}
	}
	return true;
}

bool D3DViewportRenderer::drawBounds(const MMatrix &matrix, const MBoundingBox &box, bool filled, bool useDummyGeometry,
									 float color[3],
									 LPDIRECT3DVERTEXBUFFER9 buffer /* = NULL */)
{
	// Transform from object to world space
	//
	D3DXMATRIXA16 mat = D3DXMATRIXA16
		(
		(float)matrix.matrix[0][0], (float)matrix.matrix[0][1], (float)matrix.matrix[0][2], (float)matrix.matrix[0][3],
		(float)matrix.matrix[1][0], (float)matrix.matrix[1][1], (float)matrix.matrix[1][2], (float)matrix.matrix[1][3],
		(float)matrix.matrix[2][0], (float)matrix.matrix[2][1], (float)matrix.matrix[2][2], (float)matrix.matrix[2][3],
		(float)matrix.matrix[3][0], (float)matrix.matrix[3][1], (float)matrix.matrix[3][2], (float)matrix.matrix[3][3]
		);
	m_pD3DDevice->SetTransform( D3DTS_WORLD, &mat );

	// Draw the bounding scaled and offset bounds. Handles component transforms
	//
	MPoint	minPt = box.min();
	MPoint	maxPt = box.max();
	float minVal[3] = { (float)minPt.x, (float)minPt.y, (float)minPt.z };
	float maxVal[3] = { (float)maxPt.x, (float)maxPt.y, (float)maxPt.z };
	drawCube( minVal, maxVal, filled, useDummyGeometry, color, buffer );

	return true;
}

bool D3DViewportRenderer::renderToTarget( const MRenderingInfo &renderInfo )
//
// Description:
//		Rener to off-screen render target and read back into system memory 
//		output buffer.
//
//
{
	// Direct rendering to a D3D surface
	//
	if (renderInfo.renderingAPI() == MViewportRenderer::kDirect3D)
	{
		// Maya does not support D3D currently. Would need
		// to have access to the device, and surface here
		// from an MRenderTarget. API doesn't exist, so
		// do nothing.
		return false;
	}

	//
	// Offscreen rendering
	//
	if (!m_pD3DDevice || !m_pTextureOutput || !m_pTextureInterm)
		return false;

	// START RENDER
	HRESULT hres;

	// Set colour and depth surfaces.
	//
	hres = m_pD3DDevice->SetRenderTarget( 0, m_pTextureIntermSurface );
	if (m_requireDepthStencilReadback && m_pDepthStencilSurface)
		hres = m_pD3DDevice->SetDepthStencilSurface( m_pDepthStencilSurface );

	hres = m_pD3DDevice->BeginScene();
	if (hres == D3D_OK)
	{
		// Setup projection and view matrices
		setupMatrices( renderInfo );

		// Clear the entire buffer (RGB, Depth). Leave stencil for now.
		//
		hres = m_pD3DDevice->Clear( 0, NULL, D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER, 
									D3DCOLOR_COLORVALUE(0.0f,0.0f,0.0f,1.0f), 1.0f, 0 );

		// Setup lighting
		//
		m_resourceManager.setupLighting(m_pD3DDevice);

		if (hres == D3D_OK)
		{
			// Render the scene
			drawScene(renderInfo);
		}

		m_resourceManager.cleanupLighting(m_pD3DDevice);
	}
	// END SCENE RENDER

	hres = m_pD3DDevice->EndScene();
	if (hres != D3D_OK)
		return false;

	// Do post-rendering
	postRenderToTarget();

	return true;
}

bool D3DViewportRenderer::setupMatrices( const MRenderingInfo &info )
//
// Description:
//
//		Set up camera matrices. Mechanism to check for changes in camera
//		parameters should be done before matrix setup.
//
//		Note that we *must* use a "right-handed" system (RH method 
//		versions) for computations to match what is coming from Maya.
//
{
	if (!m_pD3DDevice)
		return false;

	const MMatrix & view = info.viewMatrix(); 
	const MMatrix & projection = info.projectionMatrix();

	// Double to float conversion
	D3DXMATRIXA16 vm( (float)view.matrix[0][0], (float)view.matrix[0][1], (float)view.matrix[0][2], (float)view.matrix[0][3], 
		(float)view.matrix[1][0], (float)view.matrix[1][1], (float)view.matrix[1][2], (float)view.matrix[1][3], 
		(float)view.matrix[2][0], (float)view.matrix[2][1], (float)view.matrix[2][2], (float)view.matrix[2][3], 
		(float)view.matrix[3][0], (float)view.matrix[3][1], (float)view.matrix[3][2], (float)view.matrix[3][3]);

	D3DXMATRIXA16 pm( (float)projection.matrix[0][0], (float)projection.matrix[0][1], (float)projection.matrix[0][2], (float)projection.matrix[0][3], 
		(float)projection.matrix[1][0], (float)projection.matrix[1][1], (float)projection.matrix[1][2], (float)projection.matrix[1][3], 
		(float)projection.matrix[2][0], (float)projection.matrix[2][1], (float)projection.matrix[2][2], (float)projection.matrix[2][3], 
		(float)projection.matrix[3][0], (float)projection.matrix[3][1], (float)projection.matrix[3][2], (float)projection.matrix[3][3]);


	m_pD3DDevice->SetTransform( D3DTS_PROJECTION, &pm );
	m_pD3DDevice->SetTransform( D3DTS_VIEW, &vm  );

	return true;
}

void D3DViewportRenderer::drawFullScreenQuad(float leftU, float topV, 
											 float rightU, float bottomV,
											 float targetWidth, float targetHeight,
											 LPDIRECT3DDEVICE9 D3D)
//
// Description:
//		Draw a screen space quad.
//
{
    float width = targetWidth - 0.5f;
    float height = targetHeight - 0.5f;

    // Draw the quad
    ScreenSpaceVertex screenQuad[4];

    screenQuad[0].position = D3DXVECTOR4(-0.5f, -0.5f, 0.5f, 1.0f);
    screenQuad[0].texCoord = D3DXVECTOR2(leftU, topV);

    screenQuad[1].position = D3DXVECTOR4(width, -0.5f, 0.5f, 1.0f);
    screenQuad[1].texCoord = D3DXVECTOR2(rightU, topV);

    screenQuad[2].position = D3DXVECTOR4(-0.5f, height, 0.5f, 1.0f);
    screenQuad[2].texCoord = D3DXVECTOR2(leftU, bottomV);

    screenQuad[3].position = D3DXVECTOR4(width, height, 0.5f, 1.0f);
    screenQuad[3].texCoord = D3DXVECTOR2(rightU, bottomV);

    D3D->SetRenderState(D3DRS_ZENABLE, FALSE);
    D3D->SetFVF(ScreenSpaceVertex::FVF);
    D3D->DrawPrimitiveUP(D3DPT_TRIANGLESTRIP, 2, screenQuad, sizeof(ScreenSpaceVertex));
    D3D->SetRenderState(D3DRS_ZENABLE, TRUE);
}

bool D3DViewportRenderer::postRenderToTarget()
//
// Description:
//	Render using post-process screen effects. Uses a 2 target buffering
//	scheme to ping-pong results back and forth for each effect change.
//
{
	if (!m_pTexturePost)
		return false;

	IDirect3DSurface9 *postSurface = NULL;
	HRESULT hres = m_pTexturePost->GetSurfaceLevel( 0, &postSurface );
	if (hres != D3D_OK)
		return false;	

	IDirect3DSurface9 *currentSurface[2] = { m_pTextureIntermSurface, postSurface };
	LPDIRECT3DTEXTURE9  currentTexture[2] = { m_pTextureInterm, m_pTexturePost };

	// Render source texture into destination, and then flip back
	// and forth as required until we finish all effects
	//
	unsigned int currentTarget = 0;
	unsigned int newTarget = 0;

	// Determine the quad render size once
	//
	D3DSURFACE_DESC surfaceDesc;
	m_pTextureOutputSurface->GetDesc( &surfaceDesc );
	float quad_renderWidth = (float)surfaceDesc.Width;
	float quad_renderHeight = (float)surfaceDesc.Height;

	unsigned int numEffectsApplied = 0;

	const MStringArray &enabledEffects = m_resourceManager.getListOfEnabledPostEffects();
	const PostEffectItemList &postEffectList = m_resourceManager.getPostEffectItemList();

	unsigned int numOfEnabledEffects = enabledEffects.length();
	unsigned int numEffects = (unsigned int) postEffectList.size();

	// We don't want floating point, and we don't have any effects.
	// So there's nothing to do.
	if (!m_wantFloatingPointTargets && !numOfEnabledEffects || !numEffects)
		return false;

	// Don't need depth anymore
	m_pD3DDevice->SetRenderState( D3DRS_ZENABLE, FALSE );

	for (unsigned int m=0; m<numOfEnabledEffects ; m++)
	{
		ID3DXEffect* effect = NULL;
		PostEffectItem *effectItem = NULL;

		// Scan all existing effects item to see if the name matches
		// the enabled effect name.
		PostEffectItemList::const_iterator eit, end_eit;
		end_eit = postEffectList.end();
		for (eit = postEffectList.begin(); eit != end_eit;  eit++)
		{
			PostEffectItem *item = *eit;
			if (item)
			{
				// Enable item hase been found in the global effects list.
				if (enabledEffects[m] == item->fName)
				{
					effectItem = item;
					effect = item->fEffect;
					break;
				}
			}
		}
		if (effect != NULL)
		{
			// Flip the current render target
			newTarget = ( currentTarget + 1 ) % 2;
			m_pD3DDevice->SetRenderTarget( 0, currentSurface[newTarget] );

			// Start scene rendering
			m_pD3DDevice->BeginScene();
			{
				/* WARNING Magic word lookup */
				hres = effect->SetTechnique( "PostProcess" ); 
				if (hres != D3D_OK)
					continue;

				// Set the deltas for the kernel, if required
				/* WARNING Magic word lookup */
				effect->SetFloat( "duKernel", 1.0f / (float)quad_renderWidth );				
				effect->SetFloat( "dvKernel", 1.0f / (float)quad_renderHeight);

				// Hacks set the value for effects via Maya option variables.
				if (effectItem->fName == MString("PostProcess_ToneMapFilter"))
				{
					double value = 1.0;
					MString toneMapExp("PostProcess_ToneMapFilter_Exposure");
					if (!MGlobal::getOptionVarValue(toneMapExp, value))
						MGlobal::setOptionVarValue(toneMapExp, value);
					effect->SetFloat( "exposure", (float)value );				
				}
				else if (effectItem->fName == MString("PostProcess_SobelFilter"))
				{
					double value = 20;
					MString thickness("PostProcess_SobelFilter_edgeThickness");
					if (!MGlobal::getOptionVarValue(thickness, value))
						MGlobal::setOptionVarValue(thickness, value);
					effect->SetFloat( "edgeThickness", (float)value);	
				}

				// Start effect rendering
				unsigned int numPasses;
				hres = effect->Begin( &numPasses, 0 );
				if (hres != D3D_OK)
					continue;
				else
				{
					// Flip the current input texture
					/* WARNING Magic word lookup */
					hres = effect->SetTexture( "textureSourceColor", currentTexture[currentTarget] );
					if (hres != D3D_OK)
					{
						m_pD3DDevice->Clear( 0, NULL, D3DCLEAR_TARGET /* | D3DCLEAR_ZBUFFER */, 
							D3DCOLOR_COLORVALUE(1.0f,0.0f,0.0f,1.0f), 1.0f, 0 );

						continue;
					}

					// Loop through all passes				
					for (unsigned int p=0; p<numPasses; ++p)
					{
						hres = effect->BeginPass( p );
						if (hres != D3D_OK) 
						{
							m_pD3DDevice->Clear( 0, NULL, D3DCLEAR_TARGET /* | D3DCLEAR_ZBUFFER */, 
								D3DCOLOR_COLORVALUE(1.0f,0.0f,0.0f,1.0f), 1.0f, 0 );
							continue;
						}
						// Draw quad for the effect
						drawFullScreenQuad( 0.0f, 0.0f, 1.0f, 1.0f, 
							quad_renderWidth, quad_renderHeight, m_pD3DDevice );

						hres = effect->EndPass();
						if (hres != D3D_OK) 
						{
							m_pD3DDevice->Clear( 0, NULL, D3DCLEAR_TARGET /* | D3DCLEAR_ZBUFFER */, 
								D3DCOLOR_COLORVALUE(1.0f,0.0f,0.0f,1.0f), 1.0f, 0 );

							continue;
						}
					}
				}
				hres = effect->End();

				numEffectsApplied++;
			}
			m_pD3DDevice->SetTexture( 0, NULL);
			m_pD3DDevice->EndScene();

#if defined(_DEBUG_POST_BUFFERS_)
			bool dumpToFile= false;
			if (dumpToFile)
			{
				const char fileName[] = "c:\\temp\\d3dDump_newTarget.jpg";
				HRESULT hres = D3DXSaveSurfaceToFile( fileName, D3DXIFF_JPG,
					currentSurface[newTarget], NULL /*palette*/, NULL /*rect*/ );

				const char fileName2[] = "c:\\temp\\d3dDump_oldTarget.jpg";
				hres = D3DXSaveSurfaceToFile( fileName2, D3DXIFF_JPG,
					currentSurface[currentTarget], NULL /*palette*/, NULL /*rect*/ );

			}
#endif

			// Flip to the next render target
			//
			currentTarget = newTarget;
		}
	}

	// 
	// Copy the intermediate target to our final output target,
	// if they are not the same. If we aren't using floating point
	// then there's nothing to do.
	//
	if ((currentSurface[currentTarget] != m_pTextureIntermSurface) || 
		(m_pTextureOutputSurface != m_pTextureIntermSurface))
	{
		m_pD3DDevice->SetRenderTarget( 0, m_pTextureOutputSurface );
		m_pD3DDevice->BeginScene();
		{
			m_pD3DDevice->SetTexture( 0, currentTexture[currentTarget] );
			drawFullScreenQuad( 0.0f, 0.0f, 1.0f, 1.0f, 
				quad_renderWidth, quad_renderHeight, m_pD3DDevice );
		}
		m_pD3DDevice->EndScene();
	}

	// Release temporary surface
	if (postSurface)
		postSurface->Release();

	// Turn depth back on
	m_pD3DDevice->SetRenderState( D3DRS_ZENABLE, TRUE );

	return true;
}

bool D3DViewportRenderer::readFromTargetToSystemMemory()
//
// Description:
//		Read back render target memory into system memory to 
//		transfer back to calling code.
// 
{
	if (!m_pD3DDevice || !m_pTextureOutputSurface || m_renderWidth==0 || m_renderHeight == 0 ||
		!m_SystemMemorySurface)
		return false;

	bool readBuffer = false;

	// Dump to file option for debugging purposes.
	//
#if defined(_DUMP_SURFACE_READBACK_CONTENTS_)
	bool dumpToFile= false;
	if (dumpToFile)
	{
		const char fileName[] = "c:\\temp\\d3dDump.jpg";
		HRESULT hres = D3DXSaveSurfaceToFile( fileName, D3DXIFF_JPG,
							m_pTextureOutputSurface, NULL /*palette*/, NULL /*rect*/ );
		if (hres != D3D_OK)
		{
			MGlobal::displayWarning("Direct3D renderer : Failed to dump surface contents to file !\n");
		}
	}
#endif

	D3DSURFACE_DESC surfaceDesc;
	m_pTextureOutputSurface->GetDesc( &surfaceDesc );
	//m_renderWidth = surfaceDesc.Width;
	//m_renderHeight = surfaceDesc.Height;

	D3DLOCKED_RECT rectInfo;
	// RECTs use upper-left to lower-right scheme.
	RECT rectToRead;
	rectToRead.top = 0;
	rectToRead.left = 0;
	rectToRead.bottom = m_renderHeight;
	rectToRead.right = m_renderWidth;
	DWORD readFlags = D3DLOCK_READONLY;

	// Since the texture is in D3DPOOL_DEFAULT, need to call
	// GetRenderTargetData to get the contents back into system
	// system. Pretty gruesome code for now....
	//
	HRESULT hres = m_pD3DDevice->GetRenderTargetData( m_pTextureOutputSurface,
		m_SystemMemorySurface );

	if (hres == D3D_OK)
	{
		// Lock the system memory surface, and read back based on lock info
		// returned.
		//
		hres = m_SystemMemorySurface->LockRect( &rectInfo, &rectToRead, readFlags );
		if (hres == D3D_OK)
		{					
			INT pitch = rectInfo.Pitch;
			BYTE *data = (BYTE *)rectInfo.pBits;

			// ** Magic number warning ***
			// We use D3DFMT_A8R8G8B8 as the buffer format for now as we
			// assume 32 bits per pixel = 4 bytes per pixel. Will need to
			// change when buffer format changes possibly be float.
			//
			const unsigned int bytesPerPixel = 4;

			// Reallocate buffer block as required.
			unsigned int m_readBackBufferWidth = 0;
			unsigned int m_readBackBufferHeight = 0;
			m_readBackBuffer.getSize(m_readBackBufferWidth, m_readBackBufferHeight);

			BYTE *m_readBackBufferPtr = NULL;
			bool replaceReadBackBuffer = false;

			if (!m_readBackBufferWidth || !m_readBackBufferHeight ||
				m_readBackBufferWidth != m_renderWidth ||
				m_readBackBufferHeight != m_renderHeight)
			{
				// This crashes Maya. Need to figure out why ?????
				m_readBackBuffer.resize(m_renderWidth, m_renderHeight, false);
				m_readBackBuffer.getSize(m_readBackBufferWidth, m_readBackBufferHeight);
				if (m_readBackBufferWidth != m_renderWidth ||
					m_readBackBufferHeight != m_renderHeight)
				{
					MGlobal::displayError("D3D Renderer : Could not resize MImage buffer for readback !\n");
					return false;
				}
				m_readBackBufferPtr = (BYTE *)(m_readBackBuffer.pixels());
			}
			else
				m_readBackBufferPtr = (BYTE *)(m_readBackBuffer.pixels());

			if (m_readBackBufferPtr)
			{
				// Copy a row at a time.
				// The jump by pitch, may differ if pitch is not the same as width.
				//
				unsigned int myLineSize = m_renderWidth * bytesPerPixel;
				unsigned int offsetMyData = (m_renderHeight-1) * myLineSize;
				unsigned int offsetData = 0;

				unsigned int i;
				for ( i=0 ; i < m_renderHeight; i++ )
				{
					memcpy( m_readBackBufferPtr + offsetMyData, 
						data + offsetData, 
						myLineSize );
					offsetMyData -= myLineSize;
					offsetData += pitch;
				}

				readBuffer = true;
			}

			if (replaceReadBackBuffer)
			{
				m_readBackBuffer.setPixels( m_readBackBufferPtr, m_renderWidth,
					m_renderHeight );
				delete[] m_readBackBufferPtr;					
			}
			m_readBackBufferPtr = 0;

			// Unlock 
			hres = m_SystemMemorySurface->UnlockRect();
		}
	}
	return readBuffer;
}

#endif



